/*
 * Implementacion de Automata Finito para reconocer el simbolo '!='.
 * 
 * Basado en el proyecto descrito en: 
 * Louden, K. (1997). Compiler Construction: Principles and Practice. Boston: PWS.
 * 
 * @ Project: Compiler
 * @ File Name: NEqAutomaton.java
 */
package cml.scanning.automata;

import cml.TokenType;

public class NEqAutomaton extends Automaton {

	private enum StateType{START, IN_NEQ, DONE, ERROR};
	private StateType state;
	
	/*
	 * Constructor de la clase.
	 */
	public NEqAutomaton(){
		
		this.state = StateType.START;
		
		/*
		 * 				!			=			other
		 * START	|	IN_NEQ	|	NO_BRA	|	NO_BRA	
		 * IN_NEQ	|	[DONE]	|	DONE	|	[DONE]
		 * DONE		|	ERROR	|	ERROR	|	ERROR
		 * ERROR	|	NO_BRA	|	NO_BRA	|	NO_BRA
		 */
		
		this.successorFunc = new Branch[4][3];		
		this.successorFunc[0][0] = new Branch(StateType.IN_NEQ.ordinal(), true);
		this.successorFunc[0][1] = new Branch(this.NO_BRANCH, false);
		this.successorFunc[0][2] = new Branch(this.NO_BRANCH, false);
		
		this.successorFunc[1][0] = new Branch(StateType.DONE.ordinal(), false);
		this.successorFunc[1][1] = new Branch(StateType.DONE.ordinal(), true);
		this.successorFunc[1][2] = new Branch(StateType.DONE.ordinal(), false);
		
		this.successorFunc[2][0] = new Branch(StateType.ERROR.ordinal(), false);
		this.successorFunc[2][1] = new Branch(StateType.ERROR.ordinal(), false);
		this.successorFunc[2][2] = new Branch(StateType.ERROR.ordinal(), false);

		this.successorFunc[3][0] = new Branch(this.NO_BRANCH, false);
		this.successorFunc[3][1] = new Branch(this.NO_BRANCH, false);
		this.successorFunc[3][2] = new Branch(this.NO_BRANCH, false);		
				
		this.acceptState = new boolean[StateType.values().length];
		this.acceptState[StateType.START.ordinal()] = false;
		this.acceptState[StateType.IN_NEQ.ordinal()] = false;
		this.acceptState[StateType.DONE.ordinal()] = true;
		this.acceptState[StateType.ERROR.ordinal()] = false;

		this.tokenString = "";
		this.backtrack = false;
	}
	
	/*
	 * Ejecuta la siguiente transicion (si la hay) leyendo el caracter pasado 
	 * como parametro.
	 * 
	 * @param	char	el siguiente caracter a leer
	 * @return			<code>true</code> si ejecuto una transicion; 
	 * 					<code>false</code> en caso contrario
	 */
	@Override
	public boolean nextStep(char c) {
		
		int idx2 = (c == '!') ? 0 : (c == '=') ? 1 : 2;
		
		Branch b = this.successorFunc[state.ordinal()][idx2];
				
		if(b.getNewState() != this.NO_BRANCH)
			this.state = StateType.values()[b.getNewState()];
		else return false;
		
		if(b.save())
			tokenString += c;
		
		if(this.state == StateType.ERROR){
			
			this.currentToken = TokenType.ERROR;
			this.tokenString += ", Compiler bug";
			return false;
		}
		
		if(this.acceptState[this.state.ordinal()]){
			
			if(c == '='){
				
				backtrack = false;
				currentToken = TokenType.NEQ;
			}
			else{
				
				backtrack = true;
				currentToken = TokenType.ERROR;
			}
		}

		return true;		
	}
	
	/*
	 * Reinicia al automata.
	 */
	@Override
	public void restart(){
		
		this.state = StateType.START;
		this.tokenString = "";
		this.currentToken=null;
	}
	
	
	/*
	 * Indica si el estado actual es el inicial.
	 * 
	 * @return	<code>true</code> si es el estado inicial;
	 * 			<code>false</code> en caso contrario
	 */
	@Override
	public boolean inStartState() {

		return this.state == StateType.START;
	}
	
	/*
	 * Indica si el estado actual es de aceptacion.
	 * 
	 * @return	<code>true</code> si es estado de aceptacion;
	 * 			<code>false</code> en caso contrario
	 */
	@Override
	public boolean isDone() {
		
		return this.state == StateType.DONE;
	}
}
